﻿using System.Collections.Generic;
using UnityEngine;

namespace AI.Graph {


    public struct NodeColors {

        public Color primaryColor1;
        public Color primaryColor2;
        public Color primaryColor3;


        public NodeColors (Color primaryColor1, Color primaryColor2, Color primaryColor3) {
            this.primaryColor1 = primaryColor1;
            this.primaryColor2 = primaryColor2;
            this.primaryColor3 = primaryColor3;
        }

    }


    public abstract class Node : ScriptableObject, IListIndex {

#region Fields

        /// <summary>
        /// The rect of this node.
        /// </summary>
        [SerializeField, HideInInspector]
        private Rect m_rect;

        /// <summary>
        /// The connected controller.
        /// </summary>
        [SerializeField, HideInInspector]
        private AIController m_controller;

        /// <summary>
        /// All inputs of this node.
        /// </summary>
        [SerializeField, HideInInspector]
        private ConnectionPort[] m_inputs = new ConnectionPort[0];

        /// <summary>
        /// All outputs of this node.
        /// </summary>
        [SerializeField, HideInInspector]
        private ConnectionPort[] m_outputs = new ConnectionPort[0];

        /// <summary>
        /// All ports of this node.
        /// </summary>
        [SerializeField, HideInInspector]
        private ConnectionPort[] m_ports = new ConnectionPort[0];

        /// <summary>
        /// Should the node be rendered.
        /// </summary>
        [SerializeField, HideInInspector]
        internal bool isHidden;

#endregion


#region Properties

        /// <summary>
        /// The current index in the canvas list.
        /// </summary>
        public int listIndex { get; set; }

        internal abstract NodeColors colors { get; }

        /// <summary>
        /// The rect of this node.
        /// It's contains the position and the size.
        /// </summary>
        internal Rect rect {
            get { return m_rect; }
        }

        internal AIController controller {
            get { return m_controller; }
            set { m_controller = value; }
        }

        /// <summary>
        /// The position of the node.
        /// </summary>
        internal virtual Vector2 position {
            get { return m_rect.position; }
            set { m_rect.position = value; }
        }

        /// <summary>
        /// The size of the node.
        /// </summary>
        internal Vector2 size {
            get { return m_rect.size; }
            set { m_rect.size = value; }
        }

        /// <summary>
        /// All inputs of this node.
        /// </summary>
        public ConnectionPort[] inputs {
            get { return m_inputs; }
        }

        /// <summary>
        /// All outputs of this node.
        /// </summary>
        public ConnectionPort[] outputs {
            get { return m_outputs; }
        }

        /// <summary>
        /// All ports of this node.
        /// </summary>
        public ConnectionPort[] ports {
            get { return m_ports; }
        }

#endregion


        /// <summary>
        /// Add the given port to this node.
        /// </summary>
        /// <param name="port">The port to add.</param>
        internal void AddConnectionPort (ConnectionPort port) {
            if (port.direction == Direction.Input) {
                ArrayUtility.Add (ref m_inputs, port);
            }
            else if (port.direction == Direction.Output) {
                ArrayUtility.Add (ref m_outputs, port);
            }

            ArrayUtility.Add (ref m_ports, port);
        }


        /// <summary>
        /// Is called if the nodePort disconnect to the connected port.
        /// </summary>
        /// <param name="nodePort">The port of this node.</param>
        /// <param name="connectedPort">The connected port.</param>
        internal virtual void OnPortDisconnect (ConnectionPort nodePort, ConnectionPort connectedPort) { }


        /// <summary>
        /// Can the nodePort connect to the given otherPort. 
        /// </summary>
        /// <param name="nodePort">The port that is part of this node.</param>
        /// <param name="otherPort">The port that should connect to the nodePort.</param>
        /// <param name="error">Error if it's not a valid connection.</param>
        /// <returns>Returns true if is is a valid connection.</returns>
        internal virtual bool IsValidConnection (ConnectionPort nodePort, ConnectionPort otherPort, out string error) {
            error = "";
            return true;
        }


        /// <summary>
        /// Returns all ScriptableObjects that should be added to the AssetDatabase.
        /// </summary>
        internal ScriptableObject[] GetScriptableObjects () {
            List<ScriptableObject> scriptableObjects = new List<ScriptableObject> ();
            scriptableObjects.AddRange (ports);
            return scriptableObjects.ToArray ();
        }


        /// <summary>
        /// Call the OnStart method.
        /// </summary>
        /// <param name="aiBehaviour">The connected AIBehaviour.</param>
        internal void StartNode (AIBehaviour aiBehaviour) {
            OnStart (aiBehaviour);
        }


        /// <summary>
        /// Called if the AIBehaviour's Start Method is called.
        /// </summary>
        /// <param name="aiBehaviour">The connected AIBehaviour.</param>
        protected virtual void OnStart (AIBehaviour aiBehaviour) { }


        /// <summary>
        /// Deep clone with the given original Node.
        /// </summary>
        internal virtual void Copy (AIControllerSerializer serializer, Node originalNode) {
            m_controller = serializer.copiedController;

            m_inputs = new ConnectionPort[originalNode.m_inputs.Length];
            for (int i = 0; i < originalNode.m_inputs.Length; i++) {
                m_inputs[i] = serializer.GetCopyOf (originalNode.m_inputs[i]);
            }

            m_outputs = new ConnectionPort[originalNode.m_outputs.Length];
            for (int i = 0; i < originalNode.m_outputs.Length; i++) {
                m_outputs[i] = serializer.GetCopyOf (originalNode.m_outputs[i]);
            }

            m_ports = new ConnectionPort[originalNode.m_ports.Length];
            for (int i = 0; i < originalNode.m_ports.Length; i++) {
                m_ports[i] = serializer.GetCopyOf (originalNode.m_ports[i]);
            }
        }

    }


}